package cn.xcyy.itcc.service.impl;

import cn.xcyy.itcc.domain.KillActivity;
import cn.xcyy.itcc.domain.KillCourse;
import cn.xcyy.itcc.enums.GlodalErrorCode;
import cn.xcyy.itcc.mapper.KillActivityMapper;
import cn.xcyy.itcc.service.IKillActivityService;
import cn.xcyy.itcc.service.IKillCourseService;
import cn.xcyy.itcc.util.AssertUtil;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author whale
 * @since 2022-10-20
 */
@Service
public class KillActivityServiceImpl extends ServiceImpl<KillActivityMapper, KillActivity> implements IKillActivityService {

    @Autowired
    private IKillCourseService killCourseService;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /*
     * @Description: 发布秒杀
     * @param id: 秒杀Id
     **/
    @Override
    @Transactional
    public void publish(Long id) {
        // 1.根据Id查询活动信息
        KillActivity killActivity = selectById(id);
        AssertUtil.isNotNull(killActivity, GlodalErrorCode.NO);
        // 2.秒杀活动状态必须是待发布才可以发布活动
        boolean isStatus = KillActivity.WAIT_PUBLISHSTATUS != killActivity.getPublishStatus();
        AssertUtil.isTrue(isStatus, GlodalErrorCode.NO);
        // 3.秒杀活动的开启时间必须比当前系统时间大，否则说明秒杀已开启，那么不可开启活动
        // before()：调用者在参数之前
        boolean before = new Date().before(killActivity.getBeginTime());
        AssertUtil.isTrue(before, GlodalErrorCode.NO);
        // 4.将秒杀活动课程数据存入redis，使用map结构 {KillActivityId:map<killCourseId,killCourse>}
        List<KillCourse> killCourses = selectKillCourseByKillActivityId(killActivity.getId());
        AssertUtil.isNotEmpty(killCourses.toString(), GlodalErrorCode.NO);
        Date date = new Date();
        killCourses.forEach(killCourse ->{
            // 4.1.存储信号量数据到Redisson中
            String semaphore = killActivity.getId() + ":" + killCourse.getId();
            RSemaphore rSemaphore = redissonClient.getSemaphore(semaphore);
            boolean isOk = rSemaphore.trySetPermits(killCourse.getKillCount());
            if (isOk){
                // 4.2.存储秒杀数据到Redis中
                redisTemplate.opsForHash().put(
                        "killactivity:"+killActivity.getId(),
                        killCourse.getId().toString(),
                        killCourse
                );
                killCourse.setPublishStatus(KillActivity.ON_PUBLISHSTATUS);
                killCourse.setPublishTime(date);
                killCourseService.updateById(killCourse);
            }else {
                // 如果信号量设置失败，那么应该存储一条失败日志，钉钉或者站内信提示管理人员
            }
        });
        // 5.修改秒杀活动、秒杀活动课程的状态
        killActivity.setPublishStatus(KillActivity.ON_PUBLISHSTATUS);
        killActivity.setPublishTime(date);
        updateById(killActivity);
    }

    private List<KillCourse> selectKillCourseByKillActivityId(Long id) {
        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("activity_id",id);
        return killCourseService.selectList(wrapper);
    }


}
